#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

enum class Status
{
    NEW,          // 新创建的状态
    CONNECTED,    // 连接或者重连成功
    DISCONNECTED, // 连接丢失，需要重新连接
    CONNECTING,   // 正在重连
    CLOSED        // 重连失败，无法连接
};

const static int g_sockfd = -1;
const static int g_retry_inerval = 1;
const static int g_max_retries = 5;

class ClientConnection
{
public:
    ClientConnection(const std::string &serverip, uint16_t serverport)
        : _sockfd(g_sockfd),
          _serverip(serverip),
          _serverport(serverport),
          _sta(Status::NEW),
          _retry_interval(g_retry_inerval),
          _max_retries(g_max_retries)
    {
    }

    Status GetStatus()
    {
        return _sta;
    }

    void Connect()
    {
        // 1.创建 sockfd
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "creat sockfd error" << std::endl;
            exit(1);
        }

        // 2.bind, 不需要显式进行，connect 时OS会自动进行

        // 2.connect
        // 服务端地址信息
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr);
        // 尝试连接
        int n = connect(_sockfd, (struct sockaddr*)&server, sizeof(server));
        if (n < 0)
        {
            // 连接失败
            Disconnect(); // 清除创建的 sockfd
            _sta = Status::DISCONNECTED;
            return;
        }
        // 连接成功
        _sta = Status::CONNECTED;
    }

    void Reconnect()
    {
        _sta = Status::CONNECTING;

        // 进行规定次数的重连
        int cnt = 0;
        while(cnt < _max_retries)
        {
            ++cnt;
            Connect();
            if (_sta == Status::CONNECTED) // 重连成功
                return;

            sleep(_retry_interval);
            std::cout << "重连次数：" << cnt << ", 最大重连次数：" << _max_retries << std::endl;
        }
        // 重连失败，关闭
        _sta = Status::CLOSED;
    }

    void Process()
    {
        // 此时一定连接成功
        while (1)
        {
            // 发送数据
            sleep(1);
            std::string inbuffer = "hello server!"; 
            std::cout << "send message: " << inbuffer << std::endl;
            int n = send(_sockfd, inbuffer.c_str(), inbuffer.size(), 0);
            if (n > 0)
            {
                // 发送成功，接收数据
                char buffer[1024];
                int m = recv(_sockfd, buffer, sizeof(buffer)-1, 0);
                if (m > 0)
                {
                    // 读取成功
                    buffer[m] = 0;
                    std::cout << buffer << std::endl;
                }
                else if (m == 0)
                {
                    // server端掉线
                    std::cout << "服务端掉线，尝试重连" << std::endl;
                    _sta = Status::DISCONNECTED;
                    break;
                }
                else
                {
                    std::cout << "read error" << std::endl;
                    break;
                }
            }
            else
            {
                // 发送失败
                std::cerr << "write error" << std::endl;
                _sta = Status::CLOSED;
                break;
            }
        }
    }

    void Disconnect()
    {
        // 关闭创建的 sockfd
        if (_sockfd != g_sockfd)
        {
            close(_sockfd);
            _sockfd = g_sockfd;
            _sta = Status::CLOSED;
        }
    }

    ~ClientConnection()
    {
    }

private:
    // 服务端信息
    int _sockfd;
    std::string _serverip;
    uint16_t _serverport;

    // 重连相关
    Status _sta;
    int _retry_interval; // 重连间隔
    int _max_retries;    // 最大重连次数
};

class TcpClient
{
public:
    TcpClient(const std::string &serverip, uint16_t serverport)
        : _con(serverip, serverport)
    {
    }

    void Excute()
    {
        while (true)
        {
            switch (_con.GetStatus())
            {
            case Status::NEW:
                _con.Connect();
                break;
            case Status::DISCONNECTED:
                std::cout << "连接丢失，开始重连..." << std::endl;
                _con.Reconnect();
                break;
            case Status::CONNECTED:
                std::cout << "连接成功，开始通信..." << std::endl;
                _con.Process();
                break;
            case Status::CLOSED:
                std::cout << "重连失败，退出！" << std::endl;
                _con.Disconnect();
                return;
            default:
                break;
            }
        }
    }

    ~TcpClient()
    {
    }

private:
    ClientConnection _con;
};

// tcpclient ip port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    // 创建客户端对象，启动客户端
    TcpClient client(serverip, serverport);
    client.Excute();
    return 0;
}